/**
	This file is part of MemoryTrace.

	MemoryTrace is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

        Written by Ameziane NAIT ABDELAZIZ.
        
	MemoryTrace is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with MemoryTrace.  If not, see <http://www.gnu.org/licenses/>.
**/


/** 
 *!\file   instrumentation.cpp
 *!\author Ameziane NAIT ABDELAZIZ
 * \brief  programme principal permettant d'instrumenter un code executable
 */

#include <cstdlib>
#include <unistd.h>
#include <sched.h>
#include <stdio.h>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

#include "../compressors/CompressorProcess.h"
#include "../ipc/Pipe.h"
#include "../stack_trace/stack_trace.h"
#include "../compressors/Output.h"
#include "../ipc/MESSAGE_QUEUE.h"



#define REPLACE 1
using namespace std;


/**
 * \brief
 */
CompressorProcess cpr;
static inline void instrumentor();
static inline void writeFiles();
static void process_end(int sig);
static void process_segfault(int sig);
static bool ipc_finalized=false;


/**
 * \brief
 */
int main(int argc, char** argv) {

    char * prog;
    char * lib;
    int   status;
    
    
    if(argc>=3){
        
        //construire la liste des arguments
        prog=argv[2];
        lib =argv[1];
    }
    else{
        Output::showMessageMemoryTraceUsage();
        return EXIT_SUCCESS;
    }
    
    
    
    
    /*On met la variable d'env LD_PRELOAD à la lib de préchargement*/
    setenv("LD_PRELOAD",lib,REPLACE);
    
    pid_t child = fork();
    assert(child!=-1);
    
    if(child==0)
    {
        
        //ici le code de l'instrumenté
        char* args[argc-1];
        args[0]=prog;
        for(int i=1;i<argc-2;i++){
            args[i]=argv[i+2];
        }
        args[argc-2]=NULL;
        if (execv(prog, args)==-1){
          Output::showMessageInstrumentedProcessKilled();
          kill(getppid(),SIGINT);
          exit(1);
        }
    }
    else{
        //ici le code de l'instrumentateur (le pere)
        
        setenv("LD_PRELOAD","",REPLACE);
        	
        
        instrumentor();
        wait(&status);
        writeFiles();
    }
    
    
    return 0;
}



/**
 * 
 * @param sig
 */
void process_segfault(int sig){
    (void) signal(SIGINT, SIG_DFL);
    if(!ipc_finalized){
        Output::showMessageDebordementCapaciteInstrumenteur();
        cpr.finitIPC();
   }
}


/**
 * 
 * @param sig
 */
void process_end(int sig) {
        printf("Le processus instrumentateur va se terminer !\n");
        (void) signal(SIGINT, SIG_DFL);
        
        printf("Fermeture des ipc \n");
        /*finalisation des IPC*/
        
        if(!ipc_finalized){
           cpr.finitIPC();  
           ipc_finalized=true;
        }
        
        //Ici le code de finalization
        printf("Au revoir !\n");
 
        exit(0);
}


/**
 * \brief
 */
static inline void instrumentor(){
    printf("Ici le processus instrumentateur\n");
 
    
    /*mise en place du gestionnaire de signaux*/
    (void) signal(SIGINT, process_end);
    (void) signal(SIGSEGV,process_segfault);
    
    /*init des ipcs*/
    cpr.initIPC();
    
    //Le processus va se mettre à attendre jusque à ce qu'on le kill
    cpr.startWork();
    
}

static inline void writeFiles(){
    /*ecriture de la trace dans le fichier*/
    cpr.writeMemoryTraceToFile();
    cpr.writeBacktracesToFile(true);
    
    if(!ipc_finalized){
        cpr.finitIPC();  
        ipc_finalized=true;
    }
}